package com.gylang.springmini.factory;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ClassLoaderUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.gylang.springmini.exception.BeansException;
import com.gylang.springmini.exception.NoSuchBeanDefinitionException;
import com.gylang.springmini.lifecycle.BeanPostProcessor;
import com.gylang.springmini.registry.SimpleSingletonBeanRegistry;
import com.gylang.springmini.support.BeanDefinition;
import com.gylang.springmini.support.InvokeUtil;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.lang.Nullable;

import java.lang.annotation.Annotation;
import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * @author gylang
 * data 2021/11/15
 */
public abstract class AbstractBeanFactory extends SimpleSingletonBeanRegistry implements ConfigurableListableBeanFactory {

    protected final List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();
    private BeanFactory parentBeanFactory;

    @Override
    public Object getBean(String name) throws BeansException {

        // 获取bean
        return doGetBean(name, null, null, false, true);

    }

    @Override
    public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
        return doGetBean(name, requiredType, null, true, true);
    }

    @Override
    public Object getBean(String name, Object... args) throws BeansException {
        return doGetBean(name, null, args, true, true);
    }

    @Override
    public <T> T getBean(Class<T> requiredType) throws BeansException {
        return doGetByType(requiredType, true, null);
    }


    @Override
    public <T> T getBean(Class<T> requiredType, @Nullable Object... args) throws BeansException {
        return doGetByType(requiredType, true, args);
    }

    @Override
    public <T> T getBean(Class<T> requiredType, Boolean allowEagerInit, @Nullable Object... args) throws BeansException {
        return doGetByType(requiredType, allowEagerInit, args);
    }

    private <T> T doGetByType(Class<T> requiredType, boolean allowEagerInit, Object[] args) {
        // 当前类为非抽象类 获取其类的bean
        if (!ClassUtil.isAbstract(requiredType)) {

            String beanName = transferName(ClassUtil.getClassName(requiredType, false));
            if (null != beanName) {
                return doGetBean(beanName, requiredType, args, true, allowEagerInit);
            } else {
                throw new BeansException("不存在bean:" + requiredType);
            }

        } else {
            // 获取所有其所有的子类的实现类
            List<BeanDefinition> childBeanDefinitions = getChildBeanDefinitions(requiredType);

            if (CollUtil.size(childBeanDefinitions) > 1) {
                // bean存在多个 如果没有唯一一个主候选无法兼容
                List<BeanDefinition> primaryList = childBeanDefinitions.stream().filter(BeanDefinition::isPrimary).collect(Collectors.toList());
                if (CollUtil.size(primaryList) == 0) {
                    // 没有主候选
                    String multiBeanDefinitionMsg = CollUtil.join(childBeanDefinitions, ",", BeanDefinition::getBeanClassName);
                    throw new BeansException("存在多个[" + requiredType + "]的实现类: " + multiBeanDefinitionMsg);
                } else if (CollUtil.size(primaryList) > 1) {
                    String multiBeanDefinitionMsg = CollUtil.join(primaryList, ",", BeanDefinition::getBeanClassName);
                    // 多个主候选
                    throw new BeansException("存在多个[" + requiredType + "]的primary实现类: " + multiBeanDefinitionMsg);
                } else {
                    // 只有一个主候选
                    return doGetBean(transferName(primaryList.get(0).getBeanClassName()), requiredType, args, true, allowEagerInit);
                }
            } else if (CollUtil.size(childBeanDefinitions) == 1) {
                // 只有一个实现类
                return doGetBean(transferName(childBeanDefinitions.get(0).getBeanClassName()), requiredType, args, true, allowEagerInit);
            } else {
                throw new BeansException("bean不存在: " + requiredType);
            }
        }
    }

    @SuppressWarnings("unchecked")
    protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly, boolean allowEagerInit) {
        String beanName = transferName(name);
        // 获取单例对象
        Object singleton = getSingleton(beanName, allowEagerInit);
        Object beanInstance = null;
        if (null != singleton) {
            // 获取到的单例不为空
            beanInstance = getObjectForBeanInstance(beanName, singleton);
        } else {
            // 获取beanDefinition进行初始化
            beanInstance = instanceObjectFactory(beanName, args);
        }
        if (null == beanInstance) {
            throw new BeansException("实例bean失败: " + beanName);
        }
        if (beanInstance instanceof ObjectFactory) {
            beanInstance = ((ObjectFactory<?>) beanInstance).getObject();
            if (null == beanInstance) {
                throw new BeansException("实例bean失败: " + beanName);
            }
        }
        if (typeCheckOnly && null != requiredType) {
            if (!requiredType.isInstance(beanInstance)) {
                throw new BeansException(ClassUtil.getClassName(singleton, false) + "无法转换成" + requiredType);
            }
        }
        return (T) beanInstance;
    }


    @Override
    public <T> Supplier<T> getBeanSupplier(Class<T> requiredType, boolean allowEagerInit) {
        return () -> getBean(requiredType, allowEagerInit);
    }

    @Override
    public <T> Optional<T> getBeanOptional(Class<T> requiredType) {
        return Optional.ofNullable(getBean(requiredType));
    }

    @Override
    public <T> Optional<T> getBeanOptional(Class<T> requiredType, Object... args) {
        return Optional.of(getBean(requiredType, args));
    }

    @Override
    public boolean containsBean(String name) {
        return null != getSingleton(transferName(name), true);
    }

    @Override
    public boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
        BeanDefinition beanDefinition = getBeanDefinition(transferName(name));

        return beanDefinition.isSingleton();
    }

    @Override
    public boolean isPrototype(String name) throws NoSuchBeanDefinitionException {
        BeanDefinition beanDefinition = getBeanDefinition(transferName(name));
        return beanDefinition.isPrototype();
    }

    @Override
    public void setParentBeanFactory(BeanFactory parentBeanFactory) throws IllegalStateException {
        this.parentBeanFactory = parentBeanFactory;
    }

    @Override
    public BeanFactory getParentBeanFactory() {
        return this.parentBeanFactory;
    }

    @Override
    public boolean containsLocalBean(String name) {
        throw new UnsupportedOperationException();
    }

    @Override
    public String[] getBeanNamesForType(Class<?> type) {

        return getBeanNamesForType(type, true, true);

    }

    @Override
    public String[] getBeanNamesForType(Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
        String[] allBeanDefinition = getBeanDefinitionNames();
        List<String> beanNameList = new ArrayList<>();
        for (String beanName : allBeanDefinition) {
            BeanDefinition beanDefinition = getBeanDefinition(beanName);
            if (beanDefinition.isSingleton() && includeNonSingletons && allowEagerInit) {
                beanNameList.add(beanName);
            }
        }
        return beanNameList.toArray(new String[0]);
    }

    @Override
    public <T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException {
        return getBeansOfType(type, true, true);
    }

    @Override
    public <T> Map<String, T> getBeansOfType(Class<T> type, boolean includeNonSingletons, boolean allowEagerInit) throws BeansException {
        String[] beanNames = getBeanNamesForType(type, includeNonSingletons, allowEagerInit);
        return CollUtil.toMap(CollUtil.toList(beanNames), new HashMap<>(), bn -> bn, bn -> getBean(bn, type));
    }

    @Override
    public String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType) {
        String[] allBeanDefinition = getBeanDefinitionNames();
        List<String> beanNameList = new ArrayList<>();
        for (String beanName : allBeanDefinition) {
            BeanDefinition beanDefinition = getBeanDefinition(beanName);
            if (null == beanDefinition || beanDefinition.isAbstract()) {
                continue;
            }
            // 获取class 判断本类 基类 和 接口是否存在当前注解
            Class<?> clazz = ClassLoaderUtil.loadClass(beanDefinition.getBeanClassName());
            Annotation annotation = AnnotationUtils.findAnnotation(clazz, annotationType);
            if (null != annotation) {
                beanNameList.add(beanName);
            }

        }
        return beanNameList.toArray(new String[0]);
    }

    @Override
    public Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) throws BeansException {
        String[] beanNames = getBeanNamesForAnnotation(annotationType);
        return Arrays.stream(beanNames).collect(Collectors
                .toMap(bn -> bn, bn -> doGetBean(bn, null, null, false, true)));
    }


    private <T> List<BeanDefinition> getChildBeanDefinitions(Class<T> requiredType) {
        String[] allBeanDefinition = getBeanDefinitionNames();
        return Arrays.stream(allBeanDefinition)
                .map(this::getBeanDefinition).filter(Objects::nonNull)
                .filter(bd -> ClassUtil.isAssignable(requiredType, ClassLoaderUtil.loadClass(bd.getBeanClassName(), bd.getBeanClassClassLoad(), false)))
                .collect(Collectors.toList());
    }

    public Object instanceObjectFactory(String beanName, Object[] args) {

        Object singleton;
        synchronized (getSingletonMutex()) {
            singleton = getSingleton(beanName);
            if (null != singleton) {
                return getObjectForBeanInstance(beanName, singleton);
            }
            // 获取beanDefinition进行初始化
            BeanDefinition beanDefinition = mergeBeanDefinition(beanName);
            if (null == beanDefinition) {
                return null;
            }
            singleton = (ObjectFactory<Object>) () -> createBean(beanName, beanDefinition, args);
        }

        return singleton;
    }

    protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) {

        if (StrUtil.isAllNotEmpty(beanDefinition.getFactoryBeanName(), beanDefinition.getFactoryMethodName())) {
            // 工厂方法初始化
            // 获取工厂bean
            Object factoryBean = getBean(beanDefinition.getFactoryBeanName());
            if (null == factoryBean) {
                throw new BeansException("创建bean失败, 工厂bean[" + beanDefinition.getFactoryBeanName() + "]无法执行初始化");
            }
            return InvokeUtil.privilegedInvoke(factoryBean, beanDefinition.getFactoryMethodName(), beanDefinition.getConstructorArgumentValuesArr());
            // 执行初始化方法
        } else if (!beanDefinition.isAbstract()) {
            // 对象实例初始化
            // 生命周期方法
            Object singleton = null;
            singleton = postProcessBeforeInstantiation(beanName, ClassLoaderUtil.loadClass(beanDefinition.getBeanClassName(), beanDefinition.getBeanClassClassLoad(), false));
            if (null == singleton) {
                singleton = InvokeUtil.privilegedInvoke(() -> ReflectUtil.newInstance(ClassLoaderUtil.loadClass(beanDefinition.getBeanClassName()), beanDefinition.getConstructorArgumentValuesArr()));
            }
            singleton = postProcessAfterInstantiation(beanName, singleton);

            singleton = postProcessBeforeInitialization(beanName, singleton);
            postProcessAfterInitialization(beanName, singleton);
            return singleton;
        }

        throw new BeansException("无法进行初始化");
    }

    protected void postProcessAfterInitialization(String beanName, Object singleton) {
        beanPostProcessorList.forEach(p -> p.postProcessAfterInitialization(singleton, beanName));
    }

    protected Object postProcessBeforeInitialization(String beanName, Object singleton) {

        for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
            singleton = beanPostProcessor.postProcessBeforeInitialization(singleton, beanName);
        }
        return singleton;
    }


    protected Object postProcessAfterInstantiation(String beanName, Object singleton) {
        for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
            singleton = beanPostProcessor.postProcessAfterInstantiation(singleton, beanName);
        }
        return singleton;
    }

    protected Object postProcessBeforeInstantiation(String beanName, Class<?> clazz) {
        Object singleton = null;
        for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
            singleton = beanPostProcessor.postProcessBeforeInstantiation(clazz, beanName);
        }
        return singleton;
    }

    private Object getObjectForBeanInstance(String beanName, Object singleton) {

        if (singleton instanceof ObjectFactory) {
            // 工厂对象 用于创建对象
            if (isSingletonCurrentlyInCreation(beanName)) {
                // 对象正在创建 不允许并行操作
                throw new ConcurrentModificationException(beanName + ":不允许并行创建");
            }
            // 构建对象
            synchronized (getSingletonMutex()) {
                if (isSingletonCurrentlyInCreation(beanName)) {
                    // 对象正在创建 不允许并行操作
                    throw new ConcurrentModificationException(beanName + ":不允许并行创建");
                }
                beforeSingletonCurrentlyInCreation(beanName);
                singleton = ((ObjectFactory<?>) singleton).getObject();
                afterSingletonCurrentlyInCreation(beanName);
                return singleton;
            }
        }
        return singleton;
    }

    protected String transferName(String name) {
        return canonicalName(name);
    }

}
